#include "Texture.h"
#include "../Primitives/IPrimitive.h"
#include <iostream>
using namespace std;

typedef struct 
{
	unsigned char* data;
	int size;
	int offset;
}tImageSource;


#define CC_RGB_PREMULTIPLY_ALPHA(vr, vg, vb, va) \
	(unsigned)(((unsigned)((unsigned char)(vr) * ((unsigned char)(va) + 1)) >> 8) | \
	((unsigned)((unsigned char)(vg) * ((unsigned char)(va) + 1) >> 8) << 8) | \
	((unsigned)((unsigned char)(vb) * ((unsigned char)(va) + 1) >> 8) << 16) | \
	((unsigned)(unsigned char)(va) << 24))

static void pngReadCallback(png_structp png_ptr, png_bytep data, png_size_t length)
{
	tImageSource* isource = (tImageSource*)png_get_io_ptr(png_ptr);

	if((int)(isource->offset + length) <= isource->size)
	{
		memcpy(data, isource->data+isource->offset, length);
		isource->offset += length;
	}
	else
	{
		png_error(png_ptr, "pngReaderCallback failed");
	}
}
Texture::Texture(string filename)
{
	FILE *fp = fopen(filename.c_str(), "rb");

	fseek(fp,0,SEEK_END);
	size = ftell(fp);
	fseek(fp,0,SEEK_SET);
	unsigned char *buffer = new unsigned char[size];
	fread(buffer,sizeof(unsigned char), size,fp);
#define PNGSIGSIZE  8
	png_byte        header[PNGSIGSIZE]   = {0}; 
	png_structp     png_ptr     =   0;
	png_infop       info_ptr    = 0;
	memcpy(header, buffer, PNGSIGSIZE);
	int a = png_sig_cmp(header, 0, PNGSIGSIZE);
	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
	info_ptr = png_create_info_struct(png_ptr);
	tImageSource imageSource;
	imageSource.data    = (unsigned char*)buffer;
	imageSource.size    = size;
	imageSource.offset  = 0;
	png_set_read_fn(png_ptr, &imageSource, pngReadCallback);

	png_read_info(png_ptr, info_ptr);

	w = png_get_image_width(png_ptr, info_ptr);
	h = png_get_image_height(png_ptr, info_ptr);
	_wXh = w * h;
	int m_nBitsPerComponent = png_get_bit_depth(png_ptr, info_ptr);
	png_uint_32 color_type = png_get_color_type(png_ptr, info_ptr);

	if (color_type == PNG_COLOR_TYPE_PALETTE)
	{
		png_set_palette_to_rgb(png_ptr);
	}
	// low-bit-depth grayscale images are to be expanded to 8 bits
	if (color_type == PNG_COLOR_TYPE_GRAY && m_nBitsPerComponent < 8)
	{
		png_set_expand_gray_1_2_4_to_8(png_ptr);
	}
	// expand any tRNS chunk data into a full alpha channel
	if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
	{
		png_set_tRNS_to_alpha(png_ptr);
	}  
	// reduce images with 16-bit samples to 8 bits
	if (m_nBitsPerComponent == 16)
	{
		png_set_strip_16(png_ptr);            
	} 
	// expand grayscale images to RGB
	if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
	{
		png_set_gray_to_rgb(png_ptr);
	}

	png_bytep* row_pointers = (png_bytep*)malloc( sizeof(png_bytep) * h );

	png_read_update_info(png_ptr, info_ptr);

	rowbytes = png_get_rowbytes(png_ptr, info_ptr);

	data = new unsigned char[rowbytes * h];

	for (unsigned short i = 0; i < h; ++i)
	{
		row_pointers[i] = data + i*rowbytes;
	}
	png_read_image(png_ptr, row_pointers);

	png_read_end(png_ptr, NULL);

	png_uint_32 channel = rowbytes/w;
	if (channel == 4)
	{
		//m_bHasAlpha = true;
		unsigned int *tmp = (unsigned int *)data;
		for(unsigned short i = 0; i < h; i++)
		{
			for(unsigned int j = 0; j < rowbytes; j += 4)
			{
				float a = row_pointers[i][j+3]/255.0;
				int r = int(row_pointers[i][j]*a); 
				int g = int(row_pointers[i][j+1]*a); 
				int b = int(row_pointers[i][j+2]*a); 
				int color = (b&255) + ((g&255) << 8) + ((r&255) << 16) ;
				*tmp++  = color;
			}
		}

		//m_bPreMulti = true;
	}
	if (png_ptr)
	{
		png_destroy_read_struct(&png_ptr, (info_ptr) ? &info_ptr : 0, 0);
	}
}


Texture::~Texture(void)
{
}

void Texture::draw( IPixelDrawer* dr )
{
	unsigned int *tmp = (unsigned int *)data;
	for (int y = 0;y < h; y+=1){
		for (int x = 0;x < w; x+=1)
		{
			unsigned int color = tmp[y * w + x];
			dr->drawPixel(x,y,color);
		}
	}
}

COLOR Texture::getPixel(Vec3& uv)
{
	int x = uv.x * w;
	int y = uv.y * h;
	int i = y * w + x;
	if (i < _wXh && i > 0)
	{
		unsigned int *tmp = (unsigned int *)data;
		return tmp[i]; 
	}else{
		return RGBA(0,0,0,0);
	}
}
